Confronting the object
C++ adds object-oriented programming to the C language and C++ supports object-oriented programming. Classes are a core feature of C++ and are often referred to as user-defined types.
Classes are used to specify the form of an object, which contains the data representation and the methods used to manipulate the data. The data and methods in a class are called members of the class. Functions within a class are referred to as members of the class.
Classes
A class definition begins with the keyword class, followed by the name of the class. The body of the class is enclosed in a pair of brackets. The class definition must be followed by a semicolon or a list of declarations.
Definitions
class Box
{
public:
double length; // the length of the box
double breadth; // the width of the box
double height; // the height of the box
};
Create
The objects Box1 and Box2 have separate data members.
Box Box1; // declare Box1, of type Box
Box Box2; // Declare Box2, type Box
Instances
The public data members of an object of a class can be accessed using the direct member access operator (.) to access them.
#include <iostream>
using namespace std;
class Box
{
public:
double length; // length
double breadth; // width
double height; // height
};
int main( )
{
Box Box1; // declare Box1, type Box
Box Box2; // Declare Box2, type Box
double volume = 0.0; // used to store volume
// box 1 in detail
Box1.height = 5.0;
Box1.length = 6.0;
Box1.breadth = 7.0;
// box 2 detail
Box2.height = 10.0;
Box2.length = 12.0;
Box2.breadth = 13.0;
// volume of box 1
volume = Box1.height * Box1.length * Box1.breadth;
cout << "Volume of Box1:" << volume << endl;
// volume of box 2
volume = Box2.height * Box2.length * Box2.breadth;
cout << "Volume of Box2:" << volume << endl;
return 0;
}
output:
Volume of Box1: 210
Volume of Box2: 1560
Classes in detail
Constructors
The constructor of a class is a special kind of member function of the class which is executed every time a new object of the class is created.
The name of the constructor is identical to the name of the class and does not return any type, nor does it return void. constructors can be used to set initial values for certain member variables.
Constructors can also take arguments if desired and can be pluralised by overloading them.
#include <iostream>
using namespace std;
class Line
{
public:
void setLength( double len );
double getLength( void );
Line(); // This is the constructor
private:
double length;
};
// Member function definitions, including the constructor
Line::Line(void)
{
cout << "Object is being created" << endl;
}
void Line::setLength( double len )
{
length = len;
}
double Line::getLength( void )
{
return length;
}
// The main function of the program
int main( )
{
Line line;
// Set the length
line.setLength(6.0);
cout << "Length of line : " << line.getLength() << endl;
return 0;
}
Initializing a list
Suppose you have a class C with multiple fields X, Y, Z, etc. that need to be initialised. Similarly, you can use the above syntax and just use commas to separate the different fields as follows
C::C( double a, double b, double c): X(a), Y(b), Z(c)
{
....
}
Equivalent to:
C::C( double a, double b, double c)
{
X = a;
Y = b;
Z = c;
}
Copy Constructor
A copy constructor is a special kind of constructor that creates an object by using a previously created object in the same class to initialize a newly created object. Copy constructors are typically used to.
- Initialise a newly created object by using another object of the same type.
- Copy the object passing it to the function as an argument.
- Copy the object and return this object from the function.
If a copy constructor is not defined in the class, the compiler will define one itself. If the class has a pointer variable and has dynamic memory allocation, it must have a copy constructor. The most common form of the copy constructor is as follows.
classname (const classname &obj) {
// the body of the constructor
}
#include <iostream>
using namespace std;
class Line
{
public:
int getLength( void );
Line( int len ); // simple constructor
Line( const Line &obj); // copy constructor
~Line(); // destructor
private:
int *ptr;
};
// Member function definitions, including constructors
Line::Line(int len)
{
cout << "call constructor" << endl;
// allocate memory for the pointer
ptr = new int;
*ptr = len;
}
Line::Line(const Line &obj)
{
cout << "Calling copy constructor and allocating memory for pointer ptr" << endl;
ptr = new int;
*ptr = *obj.ptr; // copy value
}
Line::~Line(void)
{
cout << "Free memory" << endl;
delete ptr;
}
int Line::getLength( void )
{
return *ptr;
}
void display(Line obj)
{
cout << "line size : " << obj.getLength() << endl;
}
// The main function of the program
int main( )
{
Line line(10);
display(line);
return 0;
}
output:
Calling the constructor
Call the copy constructor and allocate memory for the pointer ptr
line size : 10
Freeing memory
Freeing memory
Destructors
A class's destructor is a special member function of the class that is executed each time the created object is deleted.
The name of the destructor is identical to the name of the class, except that it is prefixed with a wavy sign (~). It does not return any value and cannot take any arguments. The destructor helps to release resources before jumping out of the program (e.g. closing a file, freeing memory, etc.).
#include <iostream>
using namespace std;
class Line
{
public:
void setLength( double len );
double getLength( void );
Line(); // This is a constructor declaration
~Line(); // This is a destructor declaration
private:
double length;
};
// Member function definitions, including the constructor
Line::Line(void)
{
cout << "Object is being created" << endl;
}
Line::~Line(void)
{
cout << "Object is being deleted" << endl;
}
void Line::setLength( double len )
{
length = len;
}
double Line::getLength( void )
{
return length;
}
// The main function of the program
int main( )
{
Line line;
// Set the length
line.setLength(6.0);
cout << "Length of line : " << line.getLength() << endl;
return 0;
}
output:
Object is being created
Length of line : 6
Object is being deleted
Access modifiers
Data encapsulation is an important feature of object-oriented programming that prevents functions from directly accessing internal members of class types. Restrictions on access to class members are specified by marking each region public, private, protected inside the class body. The keywords public, private, protected are called access modifiers.
class Base {
public:
// public member
protected:
// protected member
private:
// private member
};
- public members: public members are accessible outside of the class in the program.
- private members: Private member variables or functions are not accessible or viewable outside of the class. Only classes and friend functions can access private members. By default, all members of a class are private.
- protected members: Protected member variables or functions are very similar to private members, but with one difference: protected members are accessible in derived classes (i.e. subclasses).
Member functions
Member functions of a class are those functions that have their definition and prototype written inside the class definition, just like any other variable in the class definition. A class member function is a member of a class that can manipulate any object of the class and can access all members of the object.
class Box
{
public:
double length; // length
double breadth; // width
double height; // height
double getVolume(void)
{
return length * breadth * height;
}
};
After declaring a function inside a class, the function can be defined outside the class using the range resolution operator ::, which must be preceded by the class name before the :: operator.
class Box
{
public:
double length; // length
double breadth; // width
double height; // height
double getVolume(void); // function must be declared inside the class
};
double Box::getVolume(void)
{
return length * breadth * height;
}
#include <iostream>
using namespace std;
class Box
{
public:
double length; // length
double breadth; // width
double height; // height
double getVolume(void)
{
return length * breadth * height;
}
// Member function declaration
void setLength( double len );
void setBreadth( double bre );
void setHeight( double hei );
};
// Member function definitions
void Box::setLength( double len )
{
length = len;
}
void Box::setBreadth( double bre )
{
breadth = bre;
}
void Box::setHeight( double hei )
{
height = hei;
}
// The main function of the program
int main( )
{
Box Box1; // Declare Box1, of type Box
Box Box2; // Declare Box2, of type Box
double volume = 0.0; // used to store volume
// box 1 in detail
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// box 2 details
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// volume of box 1
volume = Box1.getVolume();
cout << "Volume of Box1:" << volume << endl;
// volume of box 2
volume = Box2.getVolume();
cout << "Volume of Box2:" << volume << endl;
return 0;
}
output:
Volume of Box1: 210
Volume of Box2: 1560
Friend functions
A friend function of a class is defined outside the class, but has access to all private and protected members of the class. Although the prototype of a friend function appears in the definition of the class, a friend function is not a member function.
A friendship can be a function, which is called a friend function, or a class, which is called a friend class, in which case the whole class and all its members are friends.
class Box
{
double width;
public:
double length;
friend void printWidth( Box box ); // friend function
friend class ClassTwo; // friendly class
void setWidth( double wid );
};
#include <iostream>
using namespace std;
class Box
{
double width;
public:
friend void printWidth( Box box );
void setWidth( double wid );
};
// Member function definitions
void Box::setWidth( double wid )
{
width = wid;
}
// Please note: printWidth() is not a member function of any class
void printWidth( Box box )
{
/* Because printWidth() is a friend of Box, it can directly access any member of that class */
cout << "Width of box : " << box.width <<endl;
}
// The main function of the program
int main( )
{
Box box;
// Set the width using the member functions
box.setWidth(10.0);
// Use the friend function to output the width
printWidth( box );
return 0;
}
output:
Width of box : 10
Inline functions
C++ Inline functions are commonly used with classes. If a function is inlined, then at compile time the compiler will place a copy of the code for that function at each location where it is called.
The main difference with inlining is that it copies the source function code to the location where it is called, rather than performing operations such as site protection, jumping execution and then restoring the site, as with normal functions, which can make the program more efficient, more typical of the space-for-time idea.
If you want to define a function as an inline function, you need to place the keyword inline in front of the function name, and you need to define the function before calling it.
#include <iostream>
using namespace std;
inline int Max(int x, int y)
{
return (x > y)? x : y;
}
// The main function of the program
int main( )
{
cout << "Max (20,10): " << Max(20,10) << endl;
cout << "Max (0,200): " << Max(0,200) << endl;
cout << "Max (100,1010): " << Max(100,1010) << endl;
return 0;
}
output:
Max (20,10): 20
Max (0,200): 200
Max (100,1010): 1010
Static members
Class members can be defined as static using the static keyword. When we declare a class member to be static, this means that no matter how many objects of the class are created, there will only be one copy of the static member.
Static members are shared among all objects of the class. If no other initialisation statement exists, all static data is initialised to zero when the first object is created. We cannot place the initialisation of static members in the class definition, but it is possible to initialise it outside the class by redeclaring the static variable using the range resolution operator ::, as shown in the example below.
#include <iostream>
using namespace std;
class Box
{
public:
static int objectCount;
// constructor definition
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout << "Constructor called." << endl;
length = l;
breadth = b;
height = h;
// increase by 1 each time an object is created
objectCount++;
}
double Volume()
{
return length * breadth * height;
}
private:
double length; // length
double breadth; // width
double height; // height
};
// Initialize the static members of class Box
int Box::objectCount = 0;
int main(void)
{
Box Box1(3.3, 1.2, 1.5); // declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
// Output the total number of objects
cout << "Total objects: " << Box::objectCount << endl;
return 0;
}
output:
Constructor called.
Constructor called.
Total objects: 2
Static member functions
By declaring a function member as static, it is possible to separate the function from any particular object of the class. Static member functions can be called even if the class object does not exist, and static functions can be accessed by using the class name plus the scope resolution operator ::.
Static member functions can only access static member data, other static member functions and other functions external to the class.
Static member functions have a class scope and they cannot access the class's this pointer. You can use static member functions to determine whether certain objects of a class have been created.
Differences between static member functions and normal member functions:
Static member functions do not have a this pointer and can only access static members (including static member variables and static member functions).
Ordinary member functions have a this pointer and can access any member of the class; static member functions do not have a this pointer.
#include <iostream>
using namespace std;
class Box
{
public:
static int objectCount;
// constructor definition
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout << "Constructor called." << endl;
length = l;
breadth = b;
height = h;
// increase by 1 each time an object is created
objectCount++;
}
double Volume()
{
return length * breadth * height;
}
static int getCount()
{
return objectCount;
}
private:
double length; // length
double breadth; // width
double height; // height
};
// Initialize the static members of class Box
int Box::objectCount = 0;
int main(void)
{
// Output the total number of objects before creating them
cout << "Inital Stage Count: " << Box::getCount() << endl;
Box Box1(3.3, 1.2, 1.5); // declare box1
Box Box2(8.5, 6.0, 2.0); // declare box2
// Output the total number of objects after they have been created
cout << "Final Stage Count: " << Box::getCount() << endl;
return 0;
}
output:
Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2
``` text
### this pointer
In C++, every object can access its own address via the **this** pointer. The **this** pointer is an implicit parameter of all member functions. Therefore, it can be used inside member functions to point to the calling object.
Friend functions do not have the **this** pointer, because a friend is not a member of the class. Only member functions have a **this** pointer.
```cpp
#include <iostream>
using namespace std;
class Box
{
public:
// constructor definition
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout << "Constructor called." << endl;
length = l;
breadth = b;
height = h;
}
double Volume()
{
return length * breadth * height;
}
int compare(Box box)
{
return this->Volume() > box.Volume();
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
int main(void)
{
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
if(Box1.compare(Box2))
{
cout << "Box2 is smaller than Box1" <<endl;
}
else
{
cout << "Box2 is equal to or larger than Box1" <<endl;
}
return 0;
}
output:
Constructor called.
Constructor called.
Box2 is equal to or larger than Box1
Pointers to classes
A pointer to a C++ class is similar to a pointer to a structure. To access the members of a pointer to a class, you use the member access operator ->, just as you would for a pointer to a structure. As with all pointers, you must initialize the pointer before you can use it.
#include <iostream>
using namespace std;
class Box
{
public:
// constructor definition
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout << "Constructor called." << endl;
length = l;
breadth = b;
height = h;
}
double Volume()
{
return length * breadth * height;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
int main(void)
{
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
Box *ptrBox; // Declare pointer to a class.
// Save the address of the first object
ptrBox = &Box1;
// Now try to use the member access operator to access the members
cout << "Volume of Box1: " << ptrBox->Volume() << endl;
// Save the address of the second object
ptrBox = &Box2;
// now try to access the members using the member access operator
cout << "Volume of Box2: " << ptrBox->Volume() << endl;
return 0;
}
output:
Constructor called.
Constructor called.
Volume of Box1: 5.94
Volume of Box2: 102
A pointer to a C++ class is similar to a pointer to a structure. To access the members of a pointer to a class, you use the member access operator ->, just as you would for a pointer to a structure. As with all pointers, you must initialize the pointer before you can use it.
#include <iostream>
using namespace std;
class Box
{
public:
// constructor definition
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout << "Constructor called." << endl;
length = l;
breadth = b;
height = h;
}
double Volume()
{
return length * breadth * height;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
int main(void)
{
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
Box *ptrBox; // Declare pointer to a class.
// Save the address of the first object
ptrBox = &Box1;
// Now try to use the member access operator to access the members
cout << "Volume of Box1: " << ptrBox->Volume() << endl;
// Save the address of the second object
ptrBox = &Box2;
// now try to access the members using the member access operator
cout << "Volume of Box2: " << ptrBox->Volume() << endl;
return 0;
}
output:
Constructor called.
Constructor called.
Volume of Box1: 5.94
Volume of Box2: 102
Encapsulation
Encapsulation is a concept in object-oriented programming that binds data to the functions that manipulate it, thus ensuring safety from outside interference and misuse. Data encapsulation leads to another important OOP concept, that of data hiding.
Data encapsulation is a mechanism for binding data together with the functions that manipulate it, and data abstraction is a mechanism for exposing only the interface to the user while hiding the concrete implementation details.
#include <iostream>
using namespace std;
class Adder{
public:
// constructor
Adder(int i = 0)
{
total = i;
}
// Interface to the outside
void addNum(int number)
{
total += number;
}
// External interface
int getTotal()
{
return total;
};
private:
// Data hidden from the outside
int total;
};
int main( )
{
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
cout << "Total " << a.getTotal() <<endl;
return 0;
}
output:
Total 60
Inheritance
Inheritance allows us to define a class based on another class, which makes it easier to create and maintain an application. This has the effect of reusing code functionality and improving execution time. The inheritance syntax is defined as follows.
// Single inheritance
class <derived class name>: <inheritance method> <base class name>
// Multiple inheritance
class <derived class name>:<inheritance method 1> <base class name 1>,<inheritance method 2> <base class name 2>,...
{
<derived class class body>
};
#include <iostream>
using namespace std;
// base class Shape
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// Base class PaintCost
class PaintCost
{
public:
int getCost(int area)
{
return area * 70;
}
};
// Derived classes
class Rectangle: public Shape, public PaintCost
{
public:
int getArea()
{
return (width * height);
}
};
int main(void)
{
Rectangle Rect;
int area;
Rect.setWidth(5);
Rect.setHeight(7);
area = Rect.getArea();
// Output the area of the object
cout << "Total area: " << Rect.getArea() << endl;
// Output the total amount spent
cout << "Total paint cost: $" << Rect.getCost(area) << endl;
return 0;
}
output:
Total area: 35
Total paint cost: $2450
Inheritance types
There are three types of inheritance: public, protected and private, which change the access properties of the base class members accordingly.
public inheritance:
base class | public | protected | private |
---|---|---|---|
derived class | may | may | may not |
may | may | may not | may not |
protected Inheritance:
base class | public | protected | private |
---|---|---|---|
derivative | may | may | may not |
| private
private Inheritance:
base class | public | protected | private |
---|---|---|---|
derived class | may | may | may not |
may | may | may | may not |
Regardless of the type of inheritance, two things remain the same.
- private members can only be accessed by members of this class (within the class) and by friend elements, not by derived classes.
- protected members can be accessed by derived classes.
Polymorphism
Polymorphism literally means multiple forms. Polymorphism is used when there is a hierarchy between classes and when classes are related to each other by inheritance.
C++ polymorphism means that when a member function is called, a different function is executed depending on the type of the object calling the function.
#include <iostream>
using namespace std;
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};
class Rectangle: public Shape{
public:
Rectangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Rectangle class area :" <<endl;
return (width * height);
}
};
class Triangle: public Shape{
public:
Triangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Triangle class area :" <<endl;
return (width * height / 2);
}
};
// The main function of the program
int main( )
{
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);
// Store the address of the rectangle
shape = &rec;
// call the area function of the rectangle area
shape->area();
// store the address of the triangle
shape = &tri;
// call the area function for triangles area
shape->area();
return 0;
}
output:
Parent class area
Parent class area
The reason for the incorrect output is that the call to the area() function is set by the compiler to the version in the base class, this is known as static polymorphism, or static linking - the function call is prepared before the program is executed. Sometimes this is also called early binding, because the area() function is set up during compilation of the program.
The keyword virtual is now placed before the declaration of area() in the Shape class, as follows
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
virtual int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};
output:
Rectangle class area
Triangle class area
Virtual functions
A virtual function is a function declared in a base class using the keyword virtual. When redefining a virtual function defined in the base class in a derived class, the compiler is told not to statically link to that function.
What we want is to be able to select the function to call at any point in the program depending on the type of object being called, an operation known as dynamic linking, or later binding.
Pure virtual functions
You may want to define a virtual function in a base class so that you can redefine the function in a derived class to better apply to the object, but you cannot give a meaningful implementation of the virtual function in the base class, and this is where pure virtual functions come into play.
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
// pure virtual function, = 0 tells the compiler that the function has no body and that the above virtual function is a pure virtual function.
virtual int area() = 0;
};
Overloading functions
Several functions of the same name with similar functions can be declared in the same scope, but the formal parameters (meaning the number, type or order of the arguments) of these functions of the same name must be different.
#include <iostream>
using namespace std;
class printData
{
public:
void print(int i) {
cout << "The integer is: " << i << endl;
}
void print(double f) {
cout << "The floating point number is: " << f << endl;
}
void print(char c[]) {
cout << "The string is: " << c << endl;
}
};
int main(void)
{
printData pd;
// Output an integer
pd.print(5);
// Output a floating point number
pd.print(500.263);
// output a string
char c[] = "Hello C++";
pd.print(c);
return 0;
}
output:
The integer is: 5
Floating point number is: 500.263
String is: Hello C++
Overloading operators
You can redefine or overload most of C++'s built-in operators. This allows you to use custom types of operators.
Overloaded operators are functions with a special name, the name of which is formed by the keyword operator followed by the operator symbol to be overloaded. Like other functions, overloaded operators have a return type and a list of arguments.
Box operator+(const Box&);
#include <iostream>
using namespace std;
class Box
{
public:
double getVolume(void)
{
return length * breadth * height;
}
void setLength( double len )
{
length = len;
}
void setBreadth( double bre )
{
breadth = bre;
}
void setHeight( double hei )
{
height = hei;
}
// Overload the + operator to add two Box objects together
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
private:
double length; // length
double breadth; // width
double height; // height
};
// The main function of the program
int main( )
{
Box Box1; // Declare Box1, of type Box
Box Box2; // Declare Box2, of type Box
Box Box3; // Declare Box3, type Box
double volume = 0.0; // store the volume in this variable
// Box1 in detail
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// Box2 details
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// the volume of Box1
volume = Box1.getVolume();
cout << "Volume of Box1 : " << volume << endl;
// volume of Box2
volume = Box2.getVolume();
cout << "Volume of Box2 : " << volume << endl;
// Add the two objects together to get Box3
Box3 = Box1 + Box2;
// the volume of Box3
volume = Box3.getVolume();
cout << "Volume of Box3 : " << volume << endl;
return 0;
}
output:
Volume of Box1 : 210
Volume of Box2 : 1560
Volume of Box3 : 5400
Reloadable Operators
Binomial Arithmetic Operators | + (add), - (subtract), * (multiply), / (divide), % (take modulo) |
---|---|
Relational Operators | == (equal), ! = (not equal), < (less than), > (greater than >, <= (less than equal), >= (greater than equal) |
Logical operators | || (logical or), && (logical with), ! (logical not) |
monomial operators | + (positive), - (negative), * (pointer), & (take address) |
self-increasing and self-subtracting operators | ++ (self-increasing), -- (self-subtracting) |
bitwise operators | | (or by bit), & (with by bit), ~ (inverse by bit), ^ (iso by bit), << (left shift), >> (right shift) |
assignment operators | =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>= |
Space request and release | new, delete, new[ ] , delete[ ] |
Other operators | ()(function call), ->(member access), ,(comma), [](subscript) |
Non-overloadable operators
- . : member access operator
- . *, ->*: member pointer access operators
- ::: domain operators
- sizeof: length operator
- ? :: conditional operators
- #: preprocessing symbols